package ar.edu.utn.frba.proyecto.grupo101;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import junit.framework.TestCase;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import ar.com.siavu.controller.configuration.HibernateUtil;
import ar.com.siavu.controller.memory.AGSingleton;
import ar.com.siavu.domain.Actividad;
import ar.com.siavu.domain.ActividadDiaLibre;
import ar.com.siavu.domain.ActividadDiaSinProgramar;
import ar.com.siavu.domain.ActividadGuardia;
import ar.com.siavu.domain.ActividadPosta;
import ar.com.siavu.domain.EEstadoActividad;
import ar.com.siavu.domain.ETipoPosta;
import ar.com.siavu.domain.ETipoProgramacion;
import ar.com.siavu.domain.ETipoUsuario;
import ar.com.siavu.domain.Plazo;
import ar.com.siavu.domain.Posta;
import ar.com.siavu.domain.Programacion;
import ar.com.siavu.domain.Regla;
import ar.com.siavu.domain.Usuario;

public class PoblacionInicialCasi2 extends TestCase{

	public int peso;
	public int pesoActual;
	Map<Usuario, Programacion> objetosProgramacionFinal;
	public void testCargarPoblacionInicial(){
		peso=0;
		Session s= HibernateUtil.getSessionFactory().getCurrentSession();
		Transaction t= s.beginTransaction();
		
		Criteria critProgramacionesExistentes= s.createCriteria(Programacion.class);
		critProgramacionesExistentes.add(Restrictions.eq("TipoProgramacion", ETipoProgramacion.PlanVuelo.getId()));
		List<Programacion> programacionesViejas= critProgramacionesExistentes.list();
		for (Programacion programacion : programacionesViejas) {
			for (Actividad actividad : programacion.getActividades()) {
				s.delete(actividad);
			}
			s.delete(programacion);
		}
		
		Criteria critProgramacionesExistentes2= s.createCriteria(Programacion.class);
		critProgramacionesExistentes2.add(Restrictions.eq("TipoProgramacion", ETipoProgramacion.ProgramacionAsignada.getId()));
		List<Programacion> programacionesViejas2= critProgramacionesExistentes2.list();
		for (Programacion programacion : programacionesViejas2) {
			for (Actividad actividad : programacion.getActividades()) {
				s.delete(actividad);
			}
			s.delete(programacion);
		}
		
		t.commit();
		s= HibernateUtil.getSessionFactory().getCurrentSession();
		t= s.beginTransaction();
		
		Criteria critPrep= s.createCriteria(Regla.class);
		List<Regla> reglas=critPrep.list();
		AGSingleton.getInstance().setReglasPost(new HashMap<String, Integer>());
		for (Regla regla : reglas) {
			AGSingleton.getInstance().getReglasPost().put(regla.getNomenclatura(), regla.getHorasPostAterrizaje());
			
		}
		
		GregorianCalendar mesTrabajo= new GregorianCalendar();
		mesTrabajo.set(2010, GregorianCalendar.SEPTEMBER, 1);
		int ultimiDiaDelMes=mesTrabajo.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
		Criteria critPlazo= s.createCriteria(Plazo.class);
		critPlazo.add(Restrictions.eq("Mes",9));
		critPlazo.add(Restrictions.eq("Anio",2010));
		Plazo plazo= (Plazo)critPlazo.list().get(0);
		
		Criteria critPosta= s.createCriteria(Posta.class);
		critPosta.createAlias("VueloIda", "vue");
		critPosta.add(Restrictions.in("vue.Numero", new Integer[]{1182,1132,1140,1160,1302,1376,1360,1364}));
		GregorianCalendar gcInicio= new GregorianCalendar();
		gcInicio.set(2010, 9-1, 1, 0, 0);
		GregorianCalendar gcFin= new GregorianCalendar();
		gcFin.set(2010, 9-1, 30, 23, 59);
		critPosta.add(Restrictions.ge("vue.FechaSalida", gcInicio.getTime()));
		critPosta.add(Restrictions.le("vue.FechaSalida", gcFin.getTime()));
		critPosta.addOrder(Order.asc("vue.FechaSalida"));
		List<Posta> postas= critPosta.list();
		
		Criteria crit=s.createCriteria(Usuario.class);
		crit.add(Restrictions.eq("TipoUsuario", 1));
		List<Usuario> jefes= crit.list();
		Map<Usuario, Actividad> actividadesFijas= new LinkedHashMap<Usuario, Actividad>();
		Criteria actividadesFijasCrit = s.createCriteria(ActividadPosta.class);
		actividadesFijasCrit.createAlias("Posta", "post");
		actividadesFijasCrit.createAlias("post.VueloVuelta", "vv");
		actividadesFijasCrit.createAlias("post.VueloIda", "vi");
		actividadesFijasCrit.add(Restrictions.ge("vv.FechaLlegada", gcInicio.getTime()));
		actividadesFijasCrit.add(Restrictions.le("vi.FechaSalida", gcInicio.getTime()));
		actividadesFijasCrit.add(Restrictions.in("vi.Numero", new Integer[]{1182,1132,1140,1160,1302,1376,1360,1364}));
		actividadesFijasCrit.createAlias("Programacion", "prog");
		actividadesFijasCrit.add(Restrictions.eq("prog.TipoProgramacion", ETipoProgramacion.PlanVuelo.getId()));
		
		List<ActividadPosta> auxAct=actividadesFijasCrit.list();
		
		for (ActividadPosta act : auxAct) {
			actividadesFijas.put(act.getProgramacion().getUsuario(), act);			
		}
		
		Criteria preProgCrit= s.createCriteria(Actividad.class);
		preProgCrit.add(Restrictions.between("FechaInicio", gcInicio.getTime(), gcFin.getTime()));
		preProgCrit.createAlias("Programacion", "prog");
		
		preProgCrit.add(Restrictions.eq("prog.TipoProgramacion", ETipoProgramacion.PreProgramacion.getId()));
		preProgCrit.createAlias("prog.Usuario", "usuario");
		preProgCrit.add(Restrictions.eq("usuario.TipoPosta",ETipoPosta.Internacional.getId()));
		preProgCrit.add(Restrictions.eq("usuario.TipoUsuario",ETipoUsuario.JefeCabina.getId()));
		preProgCrit.createAlias("prog.Plazo", "plaz");
		preProgCrit.add(Restrictions.eq("plaz.Mes",9));
		preProgCrit.add(Restrictions.eq("plaz.Anio",2010));
		List<Actividad> actPreProg=preProgCrit.list();
		
//		Map<Usuario, List<Actividad>> preProgramaciones= new LinkedHashMap<Usuario, List<Actividad>>();
//		Map<Posta, List<Usuario>> programacionPosta= new LinkedHashMap<Posta, List<Usuario>>();
//		Map<Usuario, List<Actividad>> programacionUsuario=new LinkedHashMap<Usuario, List<Actividad>>();
//		Map<Usuario, Programacion> objetosProgramacion= new LinkedHashMap<Usuario, Programacion>();
//		for (Usuario jefe : jefes) {
//			preProgramaciones.put(jefe, new ArrayList<Actividad>());
//			programacionUsuario.put(jefe, new ArrayList<Actividad>());
//			Programacion p= new Programacion();
//			p.setActividades(new ArrayList<Actividad>());
//			p.setPlazo(plazo);
//			p.setTipoProgramacion(ETipoProgramacion.PlanVuelo.getId());
//			p.setUsuario(jefe);
//			objetosProgramacion.put(jefe, p);
//		}
//		for (Actividad actividad : actPreProg) {
//			if (actividad instanceof ActividadPosta){
//				actividad.setFechaVariable(actividad.getProgramacion().getFechasVariables());
//			}
//			preProgramaciones.get(actividad.getProgramacion().getUsuario()).add(actividad);
//		}
//		for (Entry<Usuario, List<Actividad>> actividades : preProgramaciones.entrySet()) {
//			Collections.sort(actividades.getValue());			
//		}
//		
//		for (Posta posta : postas) {
//			programacionPosta.put(posta, new ArrayList<Usuario>());
//		}
		Map<Usuario, List<Actividad>> preProgramaciones= new LinkedHashMap<Usuario, List<Actividad>>();
		for (Usuario jefe : jefes) {
			preProgramaciones.put(jefe, new ArrayList<Actividad>());
		}
		for (Actividad actividad : actPreProg) {
			if (actividad instanceof ActividadPosta){
				actividad.setFechaVariable(actividad.getProgramacion().getFechasVariables());
			}
			preProgramaciones.get(actividad.getProgramacion().getUsuario()).add(actividad);
		}
		for (Entry<Usuario, List<Actividad>> actividades : preProgramaciones.entrySet()) {
			Collections.sort(actividades.getValue());			
		}
	negrada: for (int h = 0; h < postas.size(); h++) {
			peso=0;
			
			Map<Posta, List<Usuario>> programacionPosta= new LinkedHashMap<Posta, List<Usuario>>();
			Map<Usuario, List<Actividad>> programacionUsuario=new LinkedHashMap<Usuario, List<Actividad>>();
			Map<Usuario, Programacion> objetosProgramacion= new LinkedHashMap<Usuario, Programacion>();
			for (Usuario jefe : jefes) {
				
				programacionUsuario.put(jefe, new ArrayList<Actividad>());
				Programacion p= new Programacion();
				p.setActividades(new ArrayList<Actividad>());
				p.setPlazo(plazo);
				p.setTipoProgramacion(ETipoProgramacion.PlanVuelo.getId());
				p.setUsuario(jefe);
				p.setFecha(new Date());
				p.setFechasVariables(false);
				objetosProgramacion.put(jefe, p);
			}
			
			
			
			for (Posta posta : postas) {
				programacionPosta.put(posta, new ArrayList<Usuario>());
			}
			
			for (int i = h; i < postas.size(); i++) {
				Posta posta= postas.get(i);
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + " " +cantidad + " fecha " + posta.getVueloIda().getFechaSalida() );
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos A");
				}
				
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" IDA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
			}
			for (int i = 0; i < h; i++) {
				Posta posta= postas.get(i);
//				if (posta.getPostaID()==38){
//					System.out.println("Roma");
//				}
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + cantidad + "fecha" + posta.getVueloIda().getFechaSalida() );
				//System.out.println("Usuarios seleccion posta " + usuariosSeleccionPosta.size());
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos B");
				}
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" VUELTA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					act.setDuracion(0);
					act.setEstadoActividad(EEstadoActividad.Desconocido.getId());
					act.setFechaVariable(false);
					act.setObservacion("Observacion");
					
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
				
			}
			for (Usuario jefe : jefes) {
				ActividadDiaSinProgramar acdsp= getDiaSinProgramarForUser(jefe, actividadesFijas.get(jefe),preProgramaciones.get(jefe), programacionUsuario.get(jefe));
				programacionUsuario.get(jefe).add(acdsp);
				objetosProgramacion.get(jefe).getActividades().add(acdsp);
			}
			for (int i=1; i<=ultimiDiaDelMes; i++){
				GregorianCalendar diaAProgramar=new GregorianCalendar();
				diaAProgramar.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramar.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramar.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramar.set(GregorianCalendar.HOUR_OF_DAY, 0);
				diaAProgramar.set(GregorianCalendar.MINUTE, 0);
				diaAProgramar.set(GregorianCalendar.SECOND, 1);
				GregorianCalendar diaAProgramarFin=new GregorianCalendar();
				diaAProgramarFin.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramarFin.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramarFin.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramarFin.set(GregorianCalendar.HOUR_OF_DAY, 23);
				diaAProgramarFin.set(GregorianCalendar.MINUTE, 59);
				diaAProgramarFin.set(GregorianCalendar.SECOND, 59);
				List<Usuario> guardias= getUsuariosGuardiaDia(jefes, diaAProgramar, actividadesFijas, programacionUsuario,2);
				
				for (Usuario usuario : guardias) {
					ActividadGuardia actGuardia= new ActividadGuardia();
					actGuardia.setDescripcion("GU");
					actGuardia.setFechaInicio(diaAProgramar.getTime());
					actGuardia.setFechaFin(diaAProgramarFin.getTime());
					programacionUsuario.get(usuario).add(actGuardia);
					objetosProgramacion.get(usuario).getActividades().add(actGuardia);
				}
			}
			System.out.println("Peso :" + peso);
			if (pesoActual<peso){
				pesoActual=peso;
				objetosProgramacionFinal=objetosProgramacion;
			}
			
		}
		
		negrada2: for (int h = postas.size() -1; h >= 0; h--) {
			peso=0;
			
			Map<Posta, List<Usuario>> programacionPosta= new LinkedHashMap<Posta, List<Usuario>>();
			Map<Usuario, List<Actividad>> programacionUsuario=new LinkedHashMap<Usuario, List<Actividad>>();
			Map<Usuario, Programacion> objetosProgramacion= new LinkedHashMap<Usuario, Programacion>();
			for (Usuario jefe : jefes) {
				
				programacionUsuario.put(jefe, new ArrayList<Actividad>());
				Programacion p= new Programacion();
				p.setActividades(new ArrayList<Actividad>());
				p.setPlazo(plazo);
				p.setTipoProgramacion(ETipoProgramacion.PlanVuelo.getId());
				p.setUsuario(jefe);
				p.setFecha(new Date());
				p.setFechasVariables(false);
				objetosProgramacion.put(jefe, p);
			}
			
			
			
			for (Posta posta : postas) {
				programacionPosta.put(posta, new ArrayList<Usuario>());
			}
			
			for (int i = h; i < postas.size(); i++) {
				Posta posta= postas.get(i);
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + " " +cantidad + " fecha " + posta.getVueloIda().getFechaSalida() );
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada2;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos A");
				}
				
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" IDA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
			}
			for (int i = 0; i < h; i++) {
				Posta posta= postas.get(i);
//				if (posta.getPostaID()==38){
//					System.out.println("Roma");
//				}
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + cantidad + "fecha" + posta.getVueloIda().getFechaSalida() );
				//System.out.println("Usuarios seleccion posta " + usuariosSeleccionPosta.size());
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada2;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos B");
				}
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" VUELTA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					act.setDuracion(0);
					act.setEstadoActividad(EEstadoActividad.Desconocido.getId());
					act.setFechaVariable(false);
					act.setObservacion("Observacion");
					
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
				
			}
			for (Usuario jefe : jefes) {
				ActividadDiaSinProgramar acdsp= getDiaSinProgramarForUser(jefe, actividadesFijas.get(jefe),preProgramaciones.get(jefe), programacionUsuario.get(jefe));
				programacionUsuario.get(jefe).add(acdsp);
				objetosProgramacion.get(jefe).getActividades().add(acdsp);
			}

			for (int i=1; i<=ultimiDiaDelMes; i++){
				GregorianCalendar diaAProgramar=new GregorianCalendar();
				diaAProgramar.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramar.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramar.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramar.set(GregorianCalendar.HOUR_OF_DAY, 0);
				diaAProgramar.set(GregorianCalendar.MINUTE, 0);
				diaAProgramar.set(GregorianCalendar.SECOND, 1);
				GregorianCalendar diaAProgramarFin=new GregorianCalendar();
				diaAProgramarFin.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramarFin.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramarFin.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramarFin.set(GregorianCalendar.HOUR_OF_DAY, 23);
				diaAProgramarFin.set(GregorianCalendar.MINUTE, 59);
				diaAProgramarFin.set(GregorianCalendar.SECOND, 59);
				List<Usuario> guardias= getUsuariosGuardiaDia(jefes, diaAProgramar, actividadesFijas, programacionUsuario,2);
				
				for (Usuario usuario : guardias) {
					ActividadGuardia actGuardia= new ActividadGuardia();
					actGuardia.setDescripcion("GU");
					actGuardia.setFechaInicio(diaAProgramar.getTime());
					actGuardia.setFechaFin(diaAProgramarFin.getTime());
					programacionUsuario.get(usuario).add(actGuardia);
					objetosProgramacion.get(usuario).getActividades().add(actGuardia);
				}
			}
			System.out.println("Peso :" + peso);
			if (pesoActual<peso){
				pesoActual=peso;
				objetosProgramacionFinal=objetosProgramacion;
			}
			
		}
		
		negrada3: for (int h = postas.size() -1; h >= 0; h--) {
			
			peso=0;
			
			Map<Posta, List<Usuario>> programacionPosta= new LinkedHashMap<Posta, List<Usuario>>();
			Map<Usuario, List<Actividad>> programacionUsuario=new LinkedHashMap<Usuario, List<Actividad>>();
			Map<Usuario, Programacion> objetosProgramacion= new LinkedHashMap<Usuario, Programacion>();
			for (Usuario jefe : jefes) {
				
				programacionUsuario.put(jefe, new ArrayList<Actividad>());
				Programacion p= new Programacion();
				p.setActividades(new ArrayList<Actividad>());
				p.setPlazo(plazo);
				p.setTipoProgramacion(ETipoProgramacion.PlanVuelo.getId());
				p.setUsuario(jefe);
				p.setFecha(new Date());
				p.setFechasVariables(false);
				objetosProgramacion.put(jefe, p);
			}
			
			
			
			for (Posta posta : postas) {
				programacionPosta.put(posta, new ArrayList<Usuario>());
			}
			
			for (int i = h; i >= 0 ; i--) {
				Posta posta= postas.get(i);
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + " " +cantidad + " fecha " + posta.getVueloIda().getFechaSalida() );
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada3;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos A");
				}
				
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" IDA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							actividad.setEstadoActividad(EEstadoActividad.Desconocido.getId());
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
			}
			for (int i = postas.size()-1; i > h; i--) {
				Posta posta= postas.get(i);
//				if (posta.getPostaID()==38){
//					System.out.println("Roma");
//				}
				int cantidad=1;
				//TODO: Arreglar esto
				//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
				List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
				//System.out.println("Posta " + posta.getNombre() + cantidad + "fecha" + posta.getVueloIda().getFechaSalida() );
				//System.out.println("Usuarios seleccion posta " + usuariosSeleccionPosta.size());
				if (usuariosSeleccionPosta.size()< cantidad){
					//System.out.println("Pincha en Posta " + posta.getNombre() + posta.getVueloIda().getFechaSalida());
					continue negrada3;
					//throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos B");
				}
				for (Usuario usuario : usuariosSeleccionPosta) {
//					if (posta.getPostaID()==38){
//						System.out.println(" VUELTA Usuario Asignado " + usuario.getNombreCompleto());
//					}
					ActividadPosta act= new ActividadPosta();
					act.setDescripcion(posta.getNombre());
					act.setFechaInicio(posta.getVueloIda().getFechaSalida());
					act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
					act.setPosta(posta);
					act.setProgramacion(objetosProgramacion.get(usuario));
					act.setDuracion(0);
					act.setEstadoActividad(EEstadoActividad.Desconocido.getId());
					act.setFechaVariable(false);
					act.setObservacion("Observacion");
					
					programacionUsuario.get(usuario).add(act);
					objetosProgramacion.get(usuario).getActividades().add(act);
					if (programacionUsuario.get(usuario).size()==1){
						List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
						for (Actividad actividad : diasLibres) {
							actividad.setProgramacion(objetosProgramacion.get(usuario));
							programacionUsuario.get(usuario).add(actividad);
							objetosProgramacion.get(usuario).getActividades().add(actividad);
						}
					}
				}
				
			}
			for (Usuario jefe : jefes) {
				ActividadDiaSinProgramar acdsp= getDiaSinProgramarForUser(jefe, actividadesFijas.get(jefe),preProgramaciones.get(jefe), programacionUsuario.get(jefe));
				programacionUsuario.get(jefe).add(acdsp);
				objetosProgramacion.get(jefe).getActividades().add(acdsp);
			}
			
			
			
			for (int i=1; i<=ultimiDiaDelMes; i++){
				GregorianCalendar diaAProgramar=new GregorianCalendar();
				diaAProgramar.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramar.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramar.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramar.set(GregorianCalendar.HOUR_OF_DAY, 0);
				diaAProgramar.set(GregorianCalendar.MINUTE, 0);
				diaAProgramar.set(GregorianCalendar.SECOND, 1);
				GregorianCalendar diaAProgramarFin=new GregorianCalendar();
				diaAProgramarFin.set(GregorianCalendar.YEAR, mesTrabajo.get(GregorianCalendar.YEAR));
				diaAProgramarFin.set(GregorianCalendar.MONTH, mesTrabajo.get(GregorianCalendar.MONTH));
				diaAProgramarFin.set(GregorianCalendar.DAY_OF_MONTH, i);
				diaAProgramarFin.set(GregorianCalendar.HOUR_OF_DAY, 23);
				diaAProgramarFin.set(GregorianCalendar.MINUTE, 59);
				diaAProgramarFin.set(GregorianCalendar.SECOND, 59);
				List<Usuario> guardias= getUsuariosGuardiaDia(jefes, diaAProgramar, actividadesFijas, programacionUsuario,2);
				
				for (Usuario usuario : guardias) {
					ActividadGuardia actGuardia= new ActividadGuardia();
					actGuardia.setDescripcion("GU");
					actGuardia.setFechaInicio(diaAProgramar.getTime());
					actGuardia.setFechaFin(diaAProgramarFin.getTime());
					actGuardia.setEstadoActividad(EEstadoActividad.Desconocido.getId());
					programacionUsuario.get(usuario).add(actGuardia);
					objetosProgramacion.get(usuario).getActividades().add(actGuardia);
				}
			}
			System.out.println("Peso :" + peso);
			if (pesoActual<peso){
				pesoActual=peso;
				objetosProgramacionFinal=objetosProgramacion;
			}
			
		}
//		for (Posta posta : postas) {
//			int cantidad=1;
//			//TODO: Arreglar esto
//			//int cantidad=Math.max(posta.getVueloIda().getAvion().getCantidadJefesCabinaMinimo(), posta.getVueloVuelta().getAvion().getCantidadJefesCabinaMinimo());
//			List<Usuario> usuariosSeleccionPosta=getUsuariosSeleccionPosta(posta, preProgramaciones, programacionUsuario, actividadesFijas,cantidad);
//			System.out.println("Posta " + posta.getNombre() + cantidad + "fecha" + posta.getVueloIda().getFechaSalida() );
//			if (usuariosSeleccionPosta.size()< cantidad){
//				throw new RuntimeException("No alcanza la cantidad de usuarios para los vuelos");
//			}
//			for (Usuario usuario : usuariosSeleccionPosta) {
//				ActividadPosta act= new ActividadPosta();
//				act.setDescripcion(posta.getNombre());
//				act.setFechaInicio(posta.getVueloIda().getFechaSalida());
//				act.setFechaFin(posta.getVueloVuelta().getFechaLlegada());
//				act.setPosta(posta);
//				act.setProgramacion(objetosProgramacion.get(usuario));
//				programacionUsuario.get(usuario).add(act);
//				objetosProgramacion.get(usuario).getActividades().add(act);
//				if (programacionUsuario.get(usuario).size()==1){
//					List<Actividad> diasLibres= getDiasLibresUsuario(usuario, preProgramaciones,actividadesFijas.get(usuario), programacionUsuario);
//					for (Actividad actividad : diasLibres) {
//						actividad.setProgramacion(objetosProgramacion.get(usuario));
//						programacionUsuario.get(usuario).add(actividad);
//						objetosProgramacion.get(usuario).getActividades().add(actividad);
//					}
//				}
//			}			
//		}
		
		SimpleDateFormat sdf= new SimpleDateFormat("dd.MM");
		for (Usuario usuario : jefes) {
			System.out.println(usuario.getNombreCompleto() + "(" + usuario.getLegajo() + ")");
			Collections.sort(objetosProgramacionFinal.get(usuario).getActividades());
			System.out.print("Prog ");
			for (Actividad actividad : objetosProgramacionFinal.get(usuario).getActividades()) {
				System.out.print(actividad.getDescripcion() + " " + sdf.format(actividad.getFechaInicio()) + "|" + sdf.format(actividad.getFechaFin()) + "|");
				
			}
			System.out.println();
			if (!preProgramaciones.get(usuario).isEmpty()){
				System.out.println("FV: " + (preProgramaciones.get(usuario).get(0).getProgramacion().getFechasVariables()? "SI": "NO"));
			}
			System.out.print("Prep ");
			for (Actividad actividad : preProgramaciones.get(usuario)) {
				String desc="";
				if (actividad instanceof ActividadPosta){
					ActividadPosta actp= (ActividadPosta) actividad;
					desc= actp.getPosta().getNombre();
				}
				if (actividad instanceof ActividadDiaLibre){
					desc= "DL";
				}
				if (actividad instanceof ActividadDiaSinProgramar){
					desc= "SP";
				}
				System.out.print(desc + " " + sdf.format(actividad.getFechaInicio()) + "|" + sdf.format(actividad.getFechaFin()) + "|");
				
			}
			System.out.println();
			System.out.println();
		}
		System.out.println("Peso :" + pesoActual);
		for (Entry<Usuario, Programacion> entry : objetosProgramacionFinal.entrySet()) {
			Programacion pro= entry.getValue();
			Programacion pro2= (Programacion)entry.getValue().clone();
			pro2.setTipoProgramacion(ETipoProgramacion.ProgramacionAsignada.getId());
			
			s.save(pro);
			
			for (Actividad actividad : pro.getActividades()) {
				s.save(actividad);
			}
			
			s.save(pro2);
			for (Actividad actividad : pro2.getActividades()) {
				s.save(actividad);
			}
		}
		t.commit();
	}

	private List<Usuario> getUsuariosGuardiaDia(List<Usuario> jefes,
			GregorianCalendar diaAProgramar,
			Map<Usuario, Actividad> actividadesFijas,
			Map<Usuario, List<Actividad>> programacionUsuario, int cantidadRequerida) {

		List<Usuario> result= new ArrayList<Usuario>();
		while(result.size()<cantidadRequerida){
			int indiceRandom=(int) (Math.round(Math.random()*10000)%jefes.size());
			Usuario usuarioSeleccion=jefes.get(indiceRandom);
			if (validaGuardiaActividadFija(diaAProgramar, actividadesFijas.get(usuarioSeleccion))
					&&
					validaGuardiaProgramacionActual(diaAProgramar, programacionUsuario.get(usuarioSeleccion))){
				result.add(usuarioSeleccion);
			}
		}
	
		return result;
	}

	private boolean validaGuardiaProgramacionActual(
			GregorianCalendar diaAProgramar, List<Actividad> programacionActual) {
			
			Date inicio= diaAProgramar.getTime();
			GregorianCalendar finGC= new GregorianCalendar();
			finGC.setTime(diaAProgramar.getTime());
			finGC.set(GregorianCalendar.HOUR_OF_DAY, 23);
			finGC.set(GregorianCalendar.MINUTE, 59);
			finGC.set(GregorianCalendar.SECOND, 59);
			
			Date fin= finGC.getTime();
			
			
			for (Actividad actividadProgramada : programacionActual) {
				GregorianCalendar gcFinProgramada= new GregorianCalendar();
				gcFinProgramada.setTime(actividadProgramada.getFechaFin());
				GregorianCalendar gcInicioProgramada= new GregorianCalendar();
				gcInicioProgramada.setTime(actividadProgramada.getFechaInicio());
				if (actividadProgramada instanceof ActividadPosta){
					ActividadPosta actFijaP=(ActividadPosta) actividadProgramada;
					gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre())-24);
					if (actFijaP.getPosta().getNombre().equals("FCN") || actFijaP.getPosta().getNombre().equals("AKL")){
						gcInicioProgramada.add(GregorianCalendar.DAY_OF_MONTH, -1);
					}
				}
				if (!(fin.before(gcInicioProgramada.getTime()) || inicio.after(gcFinProgramada.getTime()))){
					return false;
				}
			}
			return true;
		}
		
	public boolean validaGuardiaActividadFija(GregorianCalendar diaAProgramar, 
			Actividad actividadFija){
		if (actividadFija==null){
			return true;
		}
		GregorianCalendar gcFinProgramada= new GregorianCalendar();
		gcFinProgramada.setTime(actividadFija.getFechaFin());
		if (actividadFija instanceof ActividadPosta){
			ActividadPosta actFijaP=(ActividadPosta) actividadFija;
			gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
		}

		if (!diaAProgramar.getTime().before(gcFinProgramada.getTime())){
			return false;
		} else {
			return true;
		}

	}
	private ActividadDiaSinProgramar getDiaSinProgramarForUser(Usuario jefe, Actividad actividadFija,
			List<Actividad> preprogramacion, List<Actividad> listProgramacion) {
		ActividadDiaSinProgramar actDiaSPPedido=null;
		boolean sinProgramarPedido=false;
		for (Actividad actividad : listProgramacion) {
			if (actividad instanceof ActividadDiaSinProgramar){
				actDiaSPPedido=(ActividadDiaSinProgramar)actividad;
				sinProgramarPedido=true;
			}
		}
		if (actDiaSPPedido==null){
			actDiaSPPedido= new ActividadDiaSinProgramar();
			GregorianCalendar dia= new GregorianCalendar();
			int diaInicio= (int)Math.round((Math.random()*100))%30;
			dia.set(2010, GregorianCalendar.SEPTEMBER, diaInicio, 0, 0, 01);
			actDiaSPPedido.setFechaInicio(dia.getTime());
			dia.set(2010, GregorianCalendar.SEPTEMBER, diaInicio, 23, 59, 59);
			actDiaSPPedido.setFechaFin(dia.getTime());
			actDiaSPPedido.setDescripcion("SP");
		}
		List<ActividadDiaSinProgramar> result= new ArrayList<ActividadDiaSinProgramar>();
		procesoDiaSP(jefe, actividadFija, listProgramacion, result,
				actDiaSPPedido);
		GregorianCalendar adelante=new GregorianCalendar();
		adelante.setTime(actDiaSPPedido.getFechaInicio());
		GregorianCalendar atras=new GregorianCalendar();
		atras.setTime(actDiaSPPedido.getFechaInicio());
		if (!result.isEmpty()){
			peso+=5;
		}
		while(result.isEmpty()){
			
			
			adelante.add(GregorianCalendar.DAY_OF_MONTH, 1);
			atras.add(GregorianCalendar.DAY_OF_MONTH, -1);
			if (adelante.get(GregorianCalendar.DAY_OF_MONTH) <30){
				actDiaSPPedido.setFechaInicio(adelante.getTime());
				procesoDiaSP(jefe, actividadFija, listProgramacion, result,
						actDiaSPPedido);
			}
			if (result.isEmpty()&& atras.get(GregorianCalendar.DAY_OF_MONTH)>=1){
				actDiaSPPedido.setFechaInicio(atras.getTime());
				procesoDiaSP(jefe, actividadFija, listProgramacion, result,
						actDiaSPPedido);
			}
			if (sinProgramarPedido){
				peso-=1;
			}
		}
		return result.get(0);
	}

	private List<Actividad> getDiasLibresUsuario(Usuario usuario,
			Map<Usuario, List<Actividad>> preProgramaciones,
			Actividad actividadFija, Map<Usuario, List<Actividad>> programacionUsuario) {
		List<Actividad> result= new ArrayList<Actividad>();
		List<Actividad> preProgActual= preProgramaciones.get(usuario);
		Collections.sort(preProgActual);
		ActividadDiaLibre pedido =null;
		boolean diasLibresSinPedir=true;
		for (Actividad actividad : preProgActual) {
			if (actividad instanceof ActividadDiaLibre){
				pedido=((ActividadDiaLibre)actividad).clone();
				diasLibresSinPedir=false;
				break;
			}
		}
		
		if (pedido==null){
			ActividadDiaLibre actdl=new ActividadDiaLibre();
				GregorianCalendar dia= new GregorianCalendar();
				int diaInicio= (int)Math.round((Math.random()*100))%26;
				dia.set(2010, GregorianCalendar.SEPTEMBER, diaInicio, 0, 0, 01);
				actdl.setFechaInicio(dia.getTime());
				dia.set(2010, GregorianCalendar.SEPTEMBER, diaInicio, 23, 59, 59);
				actdl.setFechaFin(dia.getTime());
			//	actdl.setProgramacion(prog);
				actdl.setDescripcion("DL");
				
			//	prog.getActividades().add(actdl);
				pedido=actdl;
				diasLibresSinPedir=true;
			
		}
		procesoDiaLibre(usuario, actividadFija, programacionUsuario, result,
				pedido);

		GregorianCalendar adelante=new GregorianCalendar();
		adelante.setTime(pedido.getFechaInicio());
		GregorianCalendar atras=new GregorianCalendar();
		atras.setTime(pedido.getFechaInicio());
		if (!result.isEmpty()){
			peso+=15;
		}
		while(result.isEmpty()){
			
			
			adelante.add(GregorianCalendar.DAY_OF_MONTH, 1);
			atras.add(GregorianCalendar.DAY_OF_MONTH, -1);
			if (adelante.get(GregorianCalendar.DAY_OF_MONTH) <26){
				pedido.setFechaInicio(adelante.getTime());
				procesoDiaLibre(usuario, actividadFija, programacionUsuario, result,
						pedido);
			}
			if (result.isEmpty()&& atras.get(GregorianCalendar.DAY_OF_MONTH)>=1){
				pedido.setFechaInicio(atras.getTime());
				procesoDiaLibre(usuario, actividadFija, programacionUsuario, result,
						pedido);
			}
			if (!diasLibresSinPedir){
				peso-=25;
			}
		}
		// TODO Auto-generated method stub
		return result;
	}

	private void procesoDiaLibre(Usuario usuario, Actividad actividadFija,
			Map<Usuario, List<Actividad>> programacionUsuario,
			List<Actividad> result, ActividadDiaLibre pedido) {
		GregorianCalendar gcInicio= new GregorianCalendar();
		gcInicio.setTime(pedido.getFechaInicio());
		GregorianCalendar gcFin= new GregorianCalendar();
		gcFin.setTime(pedido.getFechaFin());
		//gcFin.add(GregorianCalendar.DAY_OF_MONTH, 3);
		if (validaFechasDiasLibresContraActividadesActuales(gcInicio.getTime(), gcFin.getTime(), programacionUsuario.get(usuario), actividadFija)
				&&validaFechasDiasLibresContraActividadFija(gcInicio.getTime(), gcFin.getTime(), actividadFija)){
			//for (int i=gcInicio.get(GregorianCalendar.DAY_OF_MONTH); i<gcInicio.get(GregorianCalendar.DAY_OF_MONTH)+4;i++){
				ActividadDiaLibre actdl= new ActividadDiaLibre();
				actdl.setDescripcion("DL");
				GregorianCalendar inicioDL= new GregorianCalendar();
				inicioDL.set(gcInicio.get(GregorianCalendar.YEAR),
						gcInicio.get(GregorianCalendar.MONTH),
						gcInicio.get(GregorianCalendar.DAY_OF_MONTH), 0, 0, 1);
				actdl.setFechaInicio(inicioDL.getTime());
				GregorianCalendar finDL= new GregorianCalendar();
				finDL.set(gcInicio.get(GregorianCalendar.YEAR),
						gcInicio.get(GregorianCalendar.MONTH),
						gcInicio.get(GregorianCalendar.DAY_OF_MONTH)+3, 23, 59, 59);
				actdl.setFechaFin(finDL.getTime());
				result.add(actdl);						
			//}
			
		}
	}
	
	private void procesoDiaSP(Usuario usuario, Actividad actividadFija,
			List<Actividad> programacionUsuario,
			List<ActividadDiaSinProgramar> result, ActividadDiaSinProgramar pedido) {
		GregorianCalendar gcInicio= new GregorianCalendar();
		gcInicio.setTime(pedido.getFechaInicio());
		GregorianCalendar gcFin= new GregorianCalendar();
		gcFin.setTime(pedido.getFechaFin());
		//gcFin.add(GregorianCalendar.DAY_OF_MONTH, 3);
		if (validaFechasDiasSPContraActividadesActuales(gcInicio.getTime(), gcFin.getTime(), programacionUsuario, actividadFija)
				&&validaFechasDiasLibresContraActividadFija(gcInicio.getTime(), gcFin.getTime(), actividadFija)){
			//for (int i=gcInicio.get(GregorianCalendar.DAY_OF_MONTH); i<gcInicio.get(GregorianCalendar.DAY_OF_MONTH)+4;i++){
				ActividadDiaSinProgramar actdSP= new ActividadDiaSinProgramar();
				actdSP.setDescripcion("SP");
				GregorianCalendar inicioDL= new GregorianCalendar();
				inicioDL.set(gcInicio.get(GregorianCalendar.YEAR),
						gcInicio.get(GregorianCalendar.MONTH),
						gcInicio.get(GregorianCalendar.DAY_OF_MONTH), 0, 0, 1);
				actdSP.setFechaInicio(inicioDL.getTime());
				GregorianCalendar finDL= new GregorianCalendar();
				finDL.set(gcInicio.get(GregorianCalendar.YEAR),
						gcInicio.get(GregorianCalendar.MONTH),
						gcInicio.get(GregorianCalendar.DAY_OF_MONTH), 23, 59, 59);
				actdSP.setFechaFin(finDL.getTime());
				result.add(actdSP);						
			//}
			
		}
	}

	private List<Usuario> getUsuariosSeleccionPosta(Posta posta,
			Map<Usuario, List<Actividad>> preProgramaciones,
			Map<Usuario, List<Actividad>> programacionActual,
			Map<Usuario, Actividad> actividadesFijas,
			int cantidad) {
//		GregorianCalendar gcAux= new GregorianCalendar();
//		gcAux.set(2010, GregorianCalendar.SEPTEMBER, 13);
//		if (posta.getVueloIda().getFechaSalida().after(gcAux.getTime())){
//			System.out.println("Pase el 13");			
//		}
		List<Actividad> actividadesConPosta= new ArrayList<Actividad>();
		for (Entry<Usuario, List<Actividad>> entry : preProgramaciones.entrySet()) {
			for (Actividad actividad : entry.getValue()) {
				if (actividad instanceof ActividadPosta){
					ActividadPosta actp=(ActividadPosta) actividad;
					if (actp.getPosta().equals(posta)){
						actividadesConPosta.add(actp);
					}
				}
			}
		}
		List<Usuario> usuarioConPedidoFiltrados=filtrarUsuariosParaActividad(actividadesConPosta, actividadesFijas, programacionActual);
		List<Usuario> result= new ArrayList<Usuario>();
		if(!usuarioConPedidoFiltrados.isEmpty()){
			Collections.shuffle(usuarioConPedidoFiltrados);
			usuarioConPedidoFiltrados= ordenarPorCantidaddeVuelosHechos(usuarioConPedidoFiltrados, programacionActual);
			if (cantidad<usuarioConPedidoFiltrados.size()){
				int i=0;
				for (; i<cantidad; i++) {
					result.add(usuarioConPedidoFiltrados.get(i));
					peso+=50;
					
				}
				for (; i<usuarioConPedidoFiltrados.size(); i++) {
					List<Actividad> progUsuarioPerdioActividad=programacionActual.get(usuarioConPedidoFiltrados.get(i));
					modificarFechaVariableEnActividadPerdida(progUsuarioPerdioActividad, posta);
				}
			} else {
				result.addAll(usuarioConPedidoFiltrados);
			}

		}
		if (result.size()<cantidad){
			List<Usuario> usuariosConFechasVariables=filtrarUsuariosFechasVariables(posta,preProgramaciones, actividadesFijas, programacionActual);
			if (cantidad < usuariosConFechasVariables.size() + result.size()){
				Collections.shuffle(usuariosConFechasVariables);
				usuariosConFechasVariables= ordenarPorCantidaddeVuelosHechos(usuariosConFechasVariables, programacionActual);
				int resultSize= result.size();
				List<Usuario> usuariosQueYaHicieronLaPosta = new ArrayList<Usuario>();
				for (int i=0; i<cantidad -resultSize; i++) {
					if (chequearSiElUsuarioYaHizoLaPosta(posta, usuariosConFechasVariables.get(i), programacionActual)){
						usuariosQueYaHicieronLaPosta.add(usuariosConFechasVariables.get(i));
					} else {
					   result.add(usuariosConFechasVariables.get(i));
					}
				}
				result.addAll(usuariosQueYaHicieronLaPosta);
			} else {
				result.addAll(usuariosConFechasVariables);
			}
		}
		if (result.size()<cantidad){
			List<Usuario> usuariosDisponibles=filtrarUsuariosDisponbles(posta,actividadesFijas, programacionActual, preProgramaciones);
			usuariosDisponibles= ordenarPorCantidaddeVuelosHechos(usuariosDisponibles, programacionActual);
			List<Usuario> usuariosSinDrama=new ArrayList<Usuario>();
			List<Usuario> usuariosConDrama=new ArrayList<Usuario>();
			for (Usuario usuario : usuariosDisponibles) {
				if (validaActividadContraActividadesActuales(posta, preProgramaciones.get(usuario))&& validaActividadContraPreProgramacionReversa(posta, preProgramaciones.get(usuario))){
					usuariosSinDrama.add(usuario);
				} else {
					usuariosConDrama.add(usuario);
				}
			}
			Collections.shuffle(usuariosSinDrama);
			Collections.shuffle(usuariosConDrama);
			usuariosSinDrama= ordenarPorCantidaddeVuelosHechos(usuariosSinDrama, programacionActual);
			usuariosConDrama= ordenarPorCantidaddeVuelosHechos(usuariosConDrama, programacionActual);
			usuariosSinDrama.addAll(usuariosConDrama);
			if (cantidad<= usuariosSinDrama.size() - result.size()){
				int resultSize= result.size();
				for (int i=0; i<cantidad -resultSize; i++) {
					result.add(usuariosSinDrama.get(i));
				}
			}
		}
		return result;
	}

	private boolean chequearSiElUsuarioYaHizoLaPosta(Posta posta,
			Usuario usuario, Map<Usuario, List<Actividad>> programacionActual) {

		List<Actividad> actividadesDelUsuario= programacionActual.get(usuario);
		for (Actividad actividad : actividadesDelUsuario) {
			if (actividad instanceof ActividadPosta){
				ActividadPosta actp= (ActividadPosta) actividad;
				if (actp.getPosta().getNombre().equals(posta.getNombre())){
					return true;
				}
			}
		}
		return false;
	}

	private void modificarFechaVariableEnActividadPerdida(
			List<Actividad> progUsuarioPerdioActividad, Posta posta) {
		for (Actividad actividad : progUsuarioPerdioActividad) {
			if (actividad instanceof ActividadPosta){
				ActividadPosta actp= (ActividadPosta) actividad;
				if (actp.getPosta().equals(posta)){
					actp.setFechaVariable(true);
				}
			}
			
		}
	}

	private List<Usuario> filtrarUsuariosDisponbles(Posta posta,
			Map<Usuario, Actividad> actividadesFijas,
			Map<Usuario, List<Actividad>> programacionActual, Map<Usuario, List<Actividad>> preProgramaciones) {
		List<Usuario> result= new ArrayList<Usuario>();
		List<Usuario> candidatos= new ArrayList<Usuario>();
		List<Usuario> conDiaLibre= new ArrayList<Usuario>();
		for (Entry<Usuario, List<Actividad>> entry : programacionActual.entrySet()) {
			if (validaActividadContraActividadesActuales(posta, entry.getValue()) && validaActividadContraActividadFija(posta, actividadesFijas.get(entry.getKey()))){
				candidatos.add(entry.getKey());
			}
			
		}
		for (Usuario usuario : candidatos) {
			if (validaPostaContraDiaLibre(posta, preProgramaciones.get(usuario))){
				result.add(usuario);
			} else {
				conDiaLibre.add(usuario); 
			}
			
		}
		Collections.shuffle(result);
		Collections.shuffle(conDiaLibre);
		result.addAll(conDiaLibre);
		return result;
	}

	private boolean validaPostaContraDiaLibre(Posta posta, List<Actividad> list) {
		GregorianCalendar gcFinPosta= new GregorianCalendar();
		gcFinPosta.setTime(posta.getVueloVuelta().getFechaLlegada());
		gcFinPosta.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(posta.getNombre())-24);
		for (Actividad actividad : list) {
			if (actividad instanceof ActividadDiaLibre){
				ActividadDiaLibre actdl= (ActividadDiaLibre) actividad;
				if (actdl.getFechaInicio().after(posta.getVueloIda().getFechaSalida()) && actdl.getFechaInicio().before(gcFinPosta.getTime())){
					return false;
				}
				if(actdl.getFechaFin().after(posta.getVueloIda().getFechaSalida()) && actdl.getFechaFin().before(gcFinPosta.getTime())){
					return false;
				} 
			}
		}
		return true;
	}

	private List<Usuario> filtrarUsuariosFechasVariables(Posta posta,
			Map<Usuario, List<Actividad>> preProgramaciones,
			Map<Usuario, Actividad> actividadesFijas,
			Map<Usuario, List<Actividad>> programacionActual) {
		List<Usuario> result= new ArrayList<Usuario>();
		for (Entry<Usuario, List<Actividad>> entry : preProgramaciones.entrySet()) {
			Usuario usuario= entry.getKey();
			for (Actividad actividad : entry.getValue()) {
				if (actividad instanceof ActividadPosta){
					ActividadPosta actPosta=(ActividadPosta) actividad;
				if (actividad.isFechaVariable()){
					
					if ((!actPosta.getPosta().equals(posta)) && actPosta.getPosta().getNombre().equals(posta.getNombre())){
						if (validaActividadContraActividadFija(posta,actividadesFijas.get(usuario)) && 
								validaActividadContraActividadesActuales(posta, programacionActual.get(usuario))){
							result.add(usuario);
							actividad.setFechaVariable(false);
							if (actividad.getProgramacion().getFechasVariables()){
								peso+=10;
							}
						}
						break;
					}
				}}
			}
			
		}
		return result;
	}

	private List<Usuario> filtrarUsuariosParaActividad(
			List<Actividad> actividadesConPosta,
			Map<Usuario, Actividad> actividadesFijas,
			Map<Usuario, List<Actividad>> programacionActual) {
		
		
		List<Usuario> result=new ArrayList<Usuario>();
		//TODO: validar el avion y fechas variables.
		for (Actividad actividad : actividadesConPosta) {
			Usuario usuario= actividad.getProgramacion().getUsuario();
			
			ActividadPosta act=(ActividadPosta) actividad;
			
			
			boolean valida=validaActividadContraActividadFija(actividad,actividadesFijas.get(usuario)) && 
			validaActividadContraActividadesActuales(actividad, programacionActual.get(usuario));
//			if (act.getPosta().getPostaID()==31 && !valida){
//				System.out.println("FRENA ACA PUTO");
//			}
			if (valida){
				result.add(usuario);
			}
		}
		return result;
	}

	private boolean validaActividadContraActividadesActuales(
			Actividad actividadActual,
			List<Actividad> programacionActual) {
		GregorianCalendar gcFinActual= new GregorianCalendar();
		gcFinActual.setTime(actividadActual.getFechaFin());
		if (actividadActual instanceof ActividadPosta){
			ActividadPosta actActP=(ActividadPosta) actividadActual;
			gcFinActual.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actActP.getPosta().getNombre()));
		}
		
		for (Actividad actividadProgramada : programacionActual) {
			GregorianCalendar gcFinProgramada= new GregorianCalendar();
			gcFinProgramada.setTime(actividadProgramada.getFechaFin());
			if (actividadProgramada instanceof ActividadPosta){
				ActividadPosta actFijaP=(ActividadPosta) actividadProgramada;
				gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
			}
			if (!(gcFinActual.getTime().before(actividadProgramada.getFechaInicio()) || actividadActual.getFechaInicio().after(gcFinProgramada.getTime()))){
				return false;
			}
		}
		return true;
	}
	private boolean validaFechasDiasLibresContraActividadesActuales(
			Date inicio, Date fin,
			List<Actividad> programacionActual, Actividad actividadFija) {
		
		
		for (Actividad actividadProgramada : programacionActual) {
			GregorianCalendar gcFinProgramada= new GregorianCalendar();
			gcFinProgramada.setTime(actividadProgramada.getFechaFin());
			if (actividadProgramada instanceof ActividadPosta){
				ActividadPosta actFijaP=(ActividadPosta) actividadProgramada;
				gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre())-24);
			}
			if (!(fin.before(actividadProgramada.getFechaInicio()) || inicio.after(gcFinProgramada.getTime()))){
				return false;
			}
		}
		return true;
	}
	private boolean validaFechasDiasSPContraActividadesActuales(
			Date inicio, Date fin,
			List<Actividad> programacionActual, Actividad actividadFija) {
		
		
		for (Actividad actividadProgramada : programacionActual) {
			GregorianCalendar gcFinProgramada= new GregorianCalendar();
			gcFinProgramada.setTime(actividadProgramada.getFechaFin());
			if (actividadProgramada instanceof ActividadPosta){
				ActividadPosta actFijaP=(ActividadPosta) actividadProgramada;
				gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
			}
			if (!(fin.before(actividadProgramada.getFechaInicio()) || inicio.after(gcFinProgramada.getTime()))){
				return false;
			}
		}
		return true;
	}

	private boolean validaActividadContraActividadFija(Actividad actividadActual,
			Actividad actividadFija) {
		if (actividadFija==null){
			return true;
		}
		GregorianCalendar gc= new GregorianCalendar();
		gc.setTime(actividadFija.getFechaFin());
		
		if (actividadFija instanceof ActividadPosta){
			ActividadPosta actFijaP=(ActividadPosta) actividadFija;
			gc.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
			
		}
		GregorianCalendar gcActual= new GregorianCalendar();
		gcActual.setTime(actividadActual.getFechaInicio());
		if (gcActual.after(gc)){
			return true;
		} else {
			return false;
		}
	}
	private boolean validaFechasDiasLibresContraActividadFija(Date inicio, Date fin,
			Actividad actividadFija) {
		if (actividadFija==null){
			return true;
		}
		GregorianCalendar gc= new GregorianCalendar();
		gc.setTime(actividadFija.getFechaFin());
		
		if (actividadFija instanceof ActividadPosta){
			ActividadPosta actFijaP=(ActividadPosta) actividadFija;
			gc.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre())-24);
			
		}
		GregorianCalendar gcActual= new GregorianCalendar();
		gcActual.setTime(inicio);
		if (gcActual.after(gc)){
			return true;
		} else {
			return false;
		}
	}
	
	private boolean validaActividadContraActividadFija(Posta posta,
			Actividad actividadFija) {
		if (actividadFija==null){
			return true;
		}
		GregorianCalendar gc= new GregorianCalendar();
		gc.setTime(actividadFija.getFechaFin());
		if (actividadFija instanceof ActividadPosta){
			ActividadPosta actFijaP=(ActividadPosta) actividadFija;
			gc.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
			
		}
		GregorianCalendar gcActual= new GregorianCalendar();
		gcActual.setTime(posta.getVueloIda().getFechaSalida());
		if (gcActual.after(gc)){
			return true;
		} else {
			return false;
		}
	}
	
	private boolean validaActividadContraActividadesActuales(
			Posta postaActual,
			List<Actividad> programacionActual) {
		GregorianCalendar gcFinActual= new GregorianCalendar();
		gcFinActual.setTime(postaActual.getVueloVuelta().getFechaLlegada());
		gcFinActual.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(postaActual.getNombre()));
		
		
		for (Actividad actividadProgramada : programacionActual) {
			GregorianCalendar gcFinProgramada= new GregorianCalendar();
			gcFinProgramada.setTime(actividadProgramada.getFechaFin());
			if (actividadProgramada instanceof ActividadPosta){
				ActividadPosta actFijaP=(ActividadPosta) actividadProgramada;
				gcFinProgramada.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actFijaP.getPosta().getNombre()));
			}
			if (!(gcFinActual.getTime().before(actividadProgramada.getFechaInicio()) || postaActual.getVueloIda().getFechaSalida().after(gcFinProgramada.getTime()))){
				return false;
			}
		}
		return true;
	}
	
	private List<Usuario> ordenarPorCantidaddeVuelosHechos(List<Usuario> usuarios, Map<Usuario, List<Actividad>> programacionActual){
		List<Usuario> result= new ArrayList<Usuario>();
		List<OrdenadorUsuarioDTO> auxList= new ArrayList<OrdenadorUsuarioDTO>();
		for (Usuario usuario : usuarios) {
			OrdenadorUsuarioDTO ouDTO= new OrdenadorUsuarioDTO();
			ouDTO.setUsuario(usuario);
			for (Actividad actividad : programacionActual.get(usuario)) {
				if (actividad instanceof ActividadPosta){
					ouDTO.setCantVuelos(ouDTO.getCantVuelos()+1);
				}
			}
			auxList.add(ouDTO);
		}
		Collections.sort(auxList);
		for (OrdenadorUsuarioDTO ordenadorUsuarioDTO : auxList) {
			result.add(ordenadorUsuarioDTO.getUsuario());
		}
		return result;
	}
	
	public boolean validaActividadContraPreProgramacionReversa(Posta posta, List<Actividad> preProgramacion){
		Collections.sort(preProgramacion);
		for (Actividad actividad : preProgramacion) {
			GregorianCalendar gcInicio= new GregorianCalendar();
			gcInicio.setTime(actividad.getFechaInicio());
			GregorianCalendar gcFin= new GregorianCalendar();
			gcFin.setTime(actividad.getFechaFin());
			if (actividad instanceof ActividadPosta){
				ActividadPosta actp=(ActividadPosta) actividad;
				gcFin.add(GregorianCalendar.HOUR_OF_DAY, AGSingleton.getInstance().getReglasPost().get(actp.getPosta().getNombre()));
			}
			if (gcInicio.getTime().before(posta.getVueloIda().getFechaSalida()) && gcFin.getTime().after(posta.getVueloIda().getFechaSalida())){
				return false;
			}
		}
		return true;
	}
}
